package com.galen.manual.service;

import com.galen.manual.config.MinioConfig;
import com.galen.manual.entity.FileRecord;
import io.minio.*;
import io.minio.http.Method;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.PostConstruct;
import javax.servlet.http.HttpServletRequest;
import java.io.InputStream;
import java.util.concurrent.TimeUnit;

/**
 * Minio文件存储服务
 */
@Slf4j
@Service
public class MinioService {

    private final MinioClient minioClient;
    private final String bucketName;
    private final FileRecordService fileRecordService;

    @Autowired
    public MinioService(MinioClient minioClient, MinioConfig minioConfig, FileRecordService fileRecordService) {
        this.minioClient = minioClient;
        this.bucketName = minioConfig.getBucketName();
        this.fileRecordService = fileRecordService;
    }

    /**
     * 初始化存储桶
     */
    @PostConstruct
    public void initBucket() {
        try {
            boolean exists = minioClient.bucketExists(BucketExistsArgs.builder()
                    .bucket(bucketName)
                    .build());

            if (!exists) {
                minioClient.makeBucket(MakeBucketArgs.builder()
                        .bucket(bucketName)
                        .region("us-east-1")
                        .build());
                log.info("Minio存储桶创建成功: {}", bucketName);
            } else {
                log.info("Minio存储桶已存在: {}", bucketName);
            }
        } catch (Exception e) {
            log.error("Minio存储桶初始化失败", e);
            throw new RuntimeException("Minio存储桶初始化失败: " + e.getMessage(), e);
        }
    }

    /**
     * 上传文件（带数据库记录）
     *
     * @param file 要上传的文件
     * @param objectName 对象名称（文件路径）
     * @param fileCategory 文件分类
     * @param businessId 业务关联ID（可选）
     * @param businessType 业务类型（可选）
     * @param request HTTP请求对象
     * @return 文件访问URL
     */
    public String uploadFile(MultipartFile file, String objectName, String fileCategory,
                              Long businessId, String businessType, HttpServletRequest request) {
        String fileUrl = null;
        FileRecord fileRecord = null;

        try {
            // 1. 创建文件记录（状态为上传中）
            fileRecord = fileRecordService.createFileRecord(
                file, objectName, null, fileCategory, businessId, businessType, request);

            log.info("文件记录创建成功，ID: {}, 对象名称: {}", fileRecord.getId(), objectName);

            // 2. 上传文件到Minio
            InputStream inputStream = file.getInputStream();
            minioClient.putObject(
                PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .stream(inputStream, file.getSize(), -1)
                    .contentType(file.getContentType())
                    .build()
            );

            // 3. 获取文件URL
            fileUrl = getFileUrl(objectName);

            // 4. 更新文件记录状态为成功
            fileRecordService.updateFileStatus(fileRecord.getId(), 1, null);

            log.info("文件上传成功: {}, URL: {}", objectName, fileUrl);
            return fileUrl;

        } catch (Exception e) {
            log.error("文件上传失败: {}", objectName, e);

            // 5. 更新文件记录状态为失败
            if (fileRecord != null) {
                fileRecordService.updateFileStatus(fileRecord.getId(), 2, e.getMessage());
            }

            throw new RuntimeException("文件上传失败: " + e.getMessage(), e);
        }
    }

    /**
     * 上传文件（兼容原有方法）
     *
     * @param file 要上传的文件
     * @param objectName 对象名称（文件路径）
     * @return 文件访问URL
     */
    public String uploadFile(MultipartFile file, String objectName) {
        return uploadFile(file, objectName, "uploads", null, null, null);
    }

    /**
     * 上传文件（使用输入流）
     *
     * @param inputStream 文件输入流
     * @param size 文件大小
     * @param contentType 文件类型
     * @param objectName 对象名称
     * @return 文件访问URL
     */
    public String uploadFile(InputStream inputStream, long size, String contentType, String objectName) {
        try {
            minioClient.putObject(
                PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .stream(inputStream, size, -1)
                    .contentType(contentType)
                    .build()
            );

            log.info("文件上传成功: {}", objectName);
            return getFileUrl(objectName);
        } catch (Exception e) {
            log.error("文件上传失败: {}", objectName, e);
            throw new RuntimeException("文件上传失败: " + e.getMessage(), e);
        }
    }

    /**
     * 删除文件
     *
     * @param objectName 对象名称
     */
    public void deleteFile(String objectName) {
        try {
            minioClient.removeObject(
                RemoveObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .build()
            );
            log.info("文件删除成功: {}", objectName);
        } catch (Exception e) {
            log.error("文件删除失败: {}", objectName, e);
            throw new RuntimeException("文件删除失败: " + e.getMessage(), e);
        }
    }

    /**
     * 检查文件是否存在
     *
     * @param objectName 对象名称
     * @return 是否存在
     */
    public boolean fileExists(String objectName) {
        try {
            minioClient.statObject(
                StatObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .build()
            );
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 获取文件访问URL
     *
     * @param objectName 对象名称
     * @return 文件访问URL
     */
    public String getFileUrl(String objectName) {
        try {
            return minioClient.getPresignedObjectUrl(
                GetPresignedObjectUrlArgs.builder()
                    .method(Method.GET)
                    .bucket(bucketName)
                    .object(objectName)
                    .expiry(7, TimeUnit.DAYS)
                    .build()
            );
        } catch (Exception e) {
            log.error("获取文件URL失败: {}", e.getMessage());
            throw new RuntimeException("获取文件URL失败", e);
        }
    }

    /**
     * 获取预签名下载URL（临时访问）
     *
     * @param objectName 对象名称
     * @param expiry 过期时间（秒）
     * @return 预签名URL
     */
    public String getPresignedUrl(String objectName, int expiry) {
        try {
            return minioClient.getPresignedObjectUrl(
                GetPresignedObjectUrlArgs.builder()
                    .method(Method.GET)
                    .bucket(bucketName)
                    .object(objectName)
                    .expiry(expiry, TimeUnit.SECONDS)
                    .build()
            );
        } catch (Exception e) {
            log.error("获取预签名URL失败: {}", objectName, e);
            throw new RuntimeException("获取预签名URL失败: " + e.getMessage(), e);
        }
    }

    /**
     * 获取预签名上传URL（用于前端直传）
     *
     * @param objectName 对象名称
     * @param expiry 过期时间（秒）
     * @return 预签名上传URL
     */
    public String getPresignedUploadUrl(String objectName, int expiry) {
        try {
            return minioClient.getPresignedObjectUrl(
                GetPresignedObjectUrlArgs.builder()
                    .method(Method.PUT)
                    .bucket(bucketName)
                    .object(objectName)
                    .expiry(expiry, TimeUnit.SECONDS)
                    .build()
            );
        } catch (Exception e) {
            log.error("获取预签名上传URL失败: {}", objectName, e);
            throw new RuntimeException("获取预签名上传URL失败: " + e.getMessage(), e);
        }
    }

    /**
     * 下载文件
     *
     * @param objectName 对象名称
     * @return 文件输入流
     */
    public InputStream downloadFile(String objectName) {
        try {
            return minioClient.getObject(
                GetObjectArgs.builder()
                    .bucket(bucketName)
                    .object(objectName)
                    .build()
            );
        } catch (Exception e) {
            log.error("文件下载失败: {}", objectName, e);
            throw new RuntimeException("文件下载失败: " + e.getMessage(), e);
        }
    }

    /**
     * 复制文件
     *
     * @param sourceObject 源对象名称
     * @param targetObject 目标对象名称
     */
    public void copyFile(String sourceObject, String targetObject) {
        try {
            minioClient.copyObject(
                CopyObjectArgs.builder()
                    .bucket(bucketName)
                    .object(targetObject)
                    .source(CopySource.builder()
                        .bucket(bucketName)
                        .object(sourceObject)
                        .build())
                    .build()
            );
            log.info("文件复制成功: {} -> {}", sourceObject, targetObject);
        } catch (Exception e) {
            log.error("文件复制失败: {} -> {}", sourceObject, targetObject, e);
            throw new RuntimeException("文件复制失败: " + e.getMessage(), e);
        }
    }

    /**
     * 生成唯一文件名
     *
     * @param originalFilename 原始文件名
     * @param prefix 前缀
     * @return 唯一文件名
     */
    public String generateUniqueFileName(String originalFilename, String prefix) {
        if (originalFilename == null || originalFilename.trim().isEmpty()) {
            throw new IllegalArgumentException("文件名不能为空");
        }

        // 获取文件扩展名
        int dotIndex = originalFilename.lastIndexOf(".");
        String extension = dotIndex > 0 ? originalFilename.substring(dotIndex) : "";

        // 生成时间戳和随机数组合的文件名
        long timestamp = System.currentTimeMillis();
        int random = (int) (Math.random() * 10000);

        // 组装文件名：前缀/时间戳_随机数.扩展名
        return String.format("%s/%d_%d%s",
            prefix != null ? prefix : "uploads",
            timestamp,
            random,
            extension);
    }
}